/* * Copyright 2008 Hippo. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hippoecm.tools.cli; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import javax.jcr.AccessDeniedException; import javax.jcr.InvalidItemStateException; import javax.jcr.Item; import javax.jcr.ItemExistsException; import javax.jcr.ItemNotFoundException; import javax.jcr.LoginException; import javax.jcr.NamespaceException; import javax.jcr.NamespaceRegistry; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.PropertyType; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.SimpleCredentials; import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.Value; import javax.jcr.lock.LockException; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.nodetype.NoSuchNodeTypeException; import javax.jcr.nodetype.NodeType; import javax.jcr.nodetype.NodeTypeIterator; import javax.jcr.observation.Event; import javax.jcr.observation.EventIterator; import javax.jcr.observation.EventListener; import javax.jcr.observation.ObservationManager; import javax.jcr.query.InvalidQueryException; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; import javax.jcr.query.QueryResult; import javax.jcr.version.VersionException; import org.apache.jackrabbit.commons.JcrUtils; /** * Wrapper class for commonly used jcr calls. */ public final class JcrWrapper { public static final String NOT_CONNECTED_PROMPT = "jcr-shell:>"; private static Map<String, SortedSet<String>> nodeNameCache = new HashMap<String, SortedSet<String>>(); private static Map<String, SortedSet<String>> propertyNameCache = new HashMap<String, SortedSet<String>>(); private static Object mutex = new Object(); private static EventListener cacheListener; private static String server = "http://localhost:8888/server"; private static String username = "admin"; private static char[] password = "admin".toCharArray(); private static Session session; private static Node currentNode; private static Node previousNode; private static boolean connected; private static Terminal term; private static String workspace; private JcrWrapper() { super(); } public static void setTerminal(final Terminal term) { JcrWrapper.term = term; } static void setPrompt() { if (!isConnected() || currentNode == null) { term.setCommandLinePrompt(NOT_CONNECTED_PROMPT); return; } try { // term.setCommandLinePrompt(getUsername()+"@"+getServer() + ":" + // currentNode.getPath() + ">"); term.setCommandLinePrompt(getUsername() + ":" + currentNode.getPath() + ">"); } catch (RepositoryException e) { // ignore // term.setCommandLinePrompt("jcr (error) :>"); } } private static void setCurrentNode(final Node node) { previousNode = currentNode; currentNode = node; setPrompt(); } public static boolean isConnected() { if (session != null && session.isLive()) { return true; } return connected; } public static void setConnected(final boolean connected) { JcrWrapper.connected = connected; } public static char[] getPassword() { return password.clone(); } public static void setPassword(final String password) { JcrWrapper.password = password.toCharArray(); } public static String getServer() { return server; } public static void setServer(final String server) { JcrWrapper.server = server; } public static String getUsername() { return username; } public static void setUsername(final String username) { JcrWrapper.username = username; } public static void clearCaches() { synchronized (mutex) { propertyNameCache.clear(); nodeNameCache.clear(); } } public static void removeFromCache(final String nodePath) { synchronized (mutex) { propertyNameCache.remove(nodePath); nodeNameCache.remove(nodePath); } } public static void updateCaches(EventIterator events) { Set<String> paths = new HashSet<String>(); while (events.hasNext()) { Event event = events.nextEvent(); try { String path = event.getPath(); switch (event.getType()) { case Event.NODE_ADDED: case Event.NODE_REMOVED: paths.add(path); // fall through case Event.PROPERTY_ADDED: case Event.PROPERTY_CHANGED: case Event.PROPERTY_REMOVED: path = path.substring(0, path.lastIndexOf('/')); paths.add(path); break; default: System.out.println("Unknown event type: " + event.getType()); break; } } catch (RepositoryException e) { e.printStackTrace(); } } for (String path : paths) { JcrWrapper.removeFromCache(path); } } public static String getStatus() { if (!isConnected()) { return username + "@" + server + " connected: " + connected; } else { return username + "@" + server + " session: " + session.getClass(); } } public static boolean connect() { if (isConnected()) { return true; } // get the repository login and get session try { System.out.println(); Repository repository = JcrUtils.getRepository(getServer()); session = repository.login(new SimpleCredentials(getUsername(), getPassword()), workspace); setConnected(true); setCurrentNode(session.getRootNode()); System.out.println("done."); // start listener for caches ObservationManager obMgr = session.getWorkspace().getObservationManager(); cacheListener = new EventListener() { public void onEvent(EventIterator events) { JcrWrapper.updateCaches(events); } }; obMgr.addEventListener(cacheListener, Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED, "/", true, null, null, true); clearCaches(); return true; } catch (LoginException e) { System.out.println("failed: " + e.getMessage()); } catch (RepositoryException e) { System.out.println("failed: " + e.getMessage()); } catch (ClassCastException e) { System.out.println("failed: " + e.getMessage()); } return false; } public static void refresh(final boolean keepChanges) { if (connect()) { try { session.refresh(keepChanges); clearCaches(); } catch (RepositoryException e) { e.printStackTrace(); } } } public static boolean login(String workspaceName) { workspace = workspaceName; return connect(); } public static void logout() { if (isConnected()) { try { ObservationManager obMgr = session.getWorkspace().getObservationManager(); obMgr.removeEventListener(cacheListener); } catch (RepositoryException e) { // ignore } session.logout(); setConnected(false); clearCaches(); previousNode = null; currentNode = null; setPrompt(); } } public static boolean save() { if (connect()) { try { session.save(); return true; } catch (AccessDeniedException e) { e.printStackTrace(); } catch (ItemExistsException e) { e.printStackTrace(); } catch (ConstraintViolationException e) { e.printStackTrace(); } catch (InvalidItemStateException e) { e.printStackTrace(); } catch (VersionException e) { e.printStackTrace(); } catch (LockException e) { e.printStackTrace(); } catch (NoSuchNodeTypeException e) { e.printStackTrace(); } catch (RepositoryException e) { e.printStackTrace(); } } return false; } public static Node getCurrentNode() { if (connect()) { return currentNode; } else { return null; } } public static boolean removeNode(final Node node) { try { removeFromCache(node.getPath()); if (node.getDepth() > 0) { removeFromCache(node.getParent().getPath()); } node.remove(); return true; } catch (RepositoryException e) { e.printStackTrace(); return false; } } public static boolean addNode(final Node parent, final String name, final String nodeType) { try { removeFromCache(parent.getPath()); parent.addNode(name, nodeType); return true; } catch (RepositoryException e) { e.printStackTrace(); return false; } } public static boolean moveNode(final Node srcNode, final String destAbsPath) throws PathNotFoundException, ItemExistsException { if (!connect()) { return false; } try { removeFromCache(srcNode.getPath()); removeFromCache(srcNode.getParent().getPath()); session.move(srcNode.getPath(), destAbsPath); int lastSlash = destAbsPath.lastIndexOf('/'); if (lastSlash > 0) { removeFromCache(destAbsPath.substring(0, lastSlash - 1)); } else { removeFromCache("/"); } } catch (ItemExistsException e) { throw e; } catch (PathNotFoundException e) { throw e; } catch (RepositoryException e) { e.printStackTrace(); } return false; } // public static boolean copyNode(final Node srcNode, final String destAbsPath) throws PathNotFoundException, // ItemExistsException { // if (!connect()) { // return false; // } // try { // if (session instanceof HippoSession) { // // Hippo specific.. // Node destNode = ((HippoSession) session).copy(srcNode, destAbsPath); // removeFromCache(destNode.getParent().getPath()); // } else { // System.out.println("Copy only available in HippoSessions."); // } // } catch (ItemExistsException e) { // throw e; // } catch (PathNotFoundException e) { // throw e; // } catch (RepositoryException e) { // e.printStackTrace(); // } // return false; // } // public static boolean isVirtual(Node jcrNode) { // if (jcrNode == null) { // return false; // } // if (!isHippoRepository()) { // return false; // } // // HippoNode hippoNode = (HippoNode) jcrNode; // try { // Node canonical = hippoNode.getCanonicalNode(); // if (canonical == null) { // return true; // } // return !hippoNode.getCanonicalNode().isSame(hippoNode); // } catch (RepositoryException e) { // System.out.println(e.getMessage()); // return false; // } // } public static SortedSet<String> getNodeNameList(final Node node) { if (!connect()) { return new TreeSet<String>(); } synchronized (nodeNameCache) { try { if (nodeNameCache.containsKey(node.getPath())) { return nodeNameCache.get(node.getPath()); } SortedSet<String> names = new TreeSet<String>(); if (node.getDepth() != 0) { names.add(".."); } NodeIterator iter = node.getNodes(); while (iter.hasNext()) { names.add(fullName(iter.nextNode())); } nodeNameCache.put(node.getPath(), Collections.unmodifiableSortedSet(names)); return nodeNameCache.get(node.getPath()); } catch (RepositoryException e) { e.printStackTrace(); } } return null; } public static SortedSet<String> getPropertyNameList(final Node node) { if (!connect()) { return new TreeSet<String>(); } synchronized (propertyNameCache) { try { if (propertyNameCache.containsKey(node.getPath())) { return propertyNameCache.get(node.getPath()); } SortedSet<String> names = new TreeSet<String>(); PropertyIterator iter = node.getProperties(); while (iter.hasNext()) { names.add(fullName(iter.nextProperty())); } propertyNameCache.put(node.getPath(), Collections.unmodifiableSortedSet(names)); return propertyNameCache.get(node.getPath()); } catch (RepositoryException e) { e.printStackTrace(); } } return null; } private static Node resolvePath(final String path) throws RepositoryException { if (path == null || path.length() == 0) { return currentNode; } else if (path.equals(".")) { return currentNode; } else if (path.equals("/")) { return session.getRootNode(); } else if (path.equals("..")) { return currentNode.getParent(); } else { String[] elements = path.split("\\/"); StringBuffer pathEncoded = new StringBuffer(elements[0]); for (int i = 1; i < elements.length; i++) { pathEncoded.append("/"); //if ("..".equals(elements[i])) { pathEncoded.append(elements[i]); //} else { // pathEncoded.append(NodeNameCodec.encode(elements[i])); //} } if (path.startsWith("/")) { return session.getRootNode().getNode(pathEncoded.toString().substring(1)); } Node refNode = null; if (path.indexOf('/') == -1 && currentNode.hasProperty(pathEncoded.toString())) { // try reference Property p = currentNode.getProperty(pathEncoded.toString()); if (p.getType() == PropertyType.REFERENCE) { if (p.getDefinition().isMultiple()) { Value[] vals = p.getValues(); if (vals.length > 0) { refNode = session.getNodeByUUID(p.getValues()[0].getString()); } } else { refNode = session.getNodeByUUID(p.getString()); } } } if (refNode == null) { return currentNode.getNode(pathEncoded.toString()); } else { return refNode; } } } public static boolean cdPrevious() { if (previousNode != null) { setCurrentNode(previousNode); return true; } return false; } public static boolean cd(final String path) { if (!connect()) { return false; } Node node = null; try { node = resolvePath(path); } catch (RepositoryException e) { System.out.println(e.getMessage()); } if (node != null) { setCurrentNode(node); return true; } else { return false; } } public static NodeIterator getNodes(final String path) { if (!connect()) { return null; } Node node = null; try { node = resolvePath(path); return node.getNodes(); } catch (RepositoryException e) { System.out.println(e.getMessage()); } return null; } public static PropertyIterator getProperties(final String path) { if (!connect()) { return null; } Node node = null; try { node = resolvePath(path); return node.getProperties(); } catch (RepositoryException e) { System.out.println(e.getMessage()); } return null; } // public static boolean cduuid(final UUID uuid) { // if (uuid == null) { // return false; // } // if (!connect()) { // return false; // } // Node node = null; // // try { // node = session.getNodeByUUID(uuid.toString()); // } catch (ItemNotFoundException e) { // return false; // } catch (RepositoryException e) { // e.printStackTrace(); // } // if (node != null) { // setCurrentNode(node); // return true; // } else { // return false; // } // } // // public static String finduuid(final UUID uuid) { // if (uuid == null) { // return null; // } // if (!connect()) { // return null; // } // Node node = null; // // try { // node = session.getNodeByUUID(uuid.toString()); // } catch (ItemNotFoundException e) { // return null; // } catch (RepositoryException e) { // e.printStackTrace(); // } // if (node != null) { // try { // return node.getPath(); // } catch (RepositoryException e) { // e.printStackTrace(); // return null; // } // } else { // return null; // } // } public static Map<String, String> getNamespaces() { SortedMap<String, String> namespaces = new TreeMap<String, String>(); if (!connect()) { return namespaces; } NamespaceRegistry nsReg; try { nsReg = session.getWorkspace().getNamespaceRegistry(); String[] uris = nsReg.getURIs(); for (String uri : uris) { try { if (!"".equals(uri)) { namespaces.put(uri, nsReg.getPrefix(uri)); } } catch (NamespaceException e) { System.out.println("Unable to resolve uri: " + uri); } } } catch (RepositoryException e1) { e1.printStackTrace(); } return namespaces; } public static boolean addNamespace(String prefix, String uri) { if (!connect()) { return false; } NamespaceRegistry nsReg; try { nsReg = session.getWorkspace().getNamespaceRegistry(); try { nsReg.registerNamespace(prefix, uri); return true; } catch (UnsupportedRepositoryOperationException e) { System.out.println("Not supported: " + e.getMessage()); } catch (AccessDeniedException e) { System.out.println("Not allowed: " + e.getMessage()); } } catch (RepositoryException e) { e.printStackTrace(); } return false; } public static boolean removeNamespace(String prefix) { if (!connect()) { return false; } NamespaceRegistry nsReg; try { nsReg = session.getWorkspace().getNamespaceRegistry(); try { nsReg.unregisterNamespace(prefix); return true; } catch (NamespaceException e) { System.out.println("Failed: " + e.getMessage()); } catch (UnsupportedRepositoryOperationException e) { System.out.println("Not supported: " + e.getMessage()); } catch (AccessDeniedException e) { System.out.println("Not allowed: " + e.getMessage()); } } catch (RepositoryException e) { e.printStackTrace(); } return false; } public static NodeType getNodeType(String name) { if (!connect()) { return null; } NodeType nt = null; try { nt = session.getWorkspace().getNodeTypeManager().getNodeType(name); } catch (NoSuchNodeTypeException e) { System.out.println("No such node type: " + name); } catch (RepositoryException e) { e.printStackTrace(); } return nt; } public static SortedSet<String> getNodeTypes(String type) { if (!connect()) { return null; } try { NodeTypeIterator iter; SortedSet<String> types = new TreeSet<String>(); if ("primary".equals(type)) { iter = session.getWorkspace().getNodeTypeManager().getPrimaryNodeTypes(); } else if ("mixin".equals(type)) { iter = session.getWorkspace().getNodeTypeManager().getMixinNodeTypes(); } else { iter = session.getWorkspace().getNodeTypeManager().getAllNodeTypes(); } while (iter.hasNext()) { types.add(iter.nextNodeType().getName()); } return types; } catch (RepositoryException e) { e.printStackTrace(); return null; } } public static NodeIterator query(final String statement, final String language) throws InvalidQueryException { if (statement == null) { return null; } if (!connect()) { return null; } QueryManager qm; try { qm = session.getWorkspace().getQueryManager(); Query q = qm.createQuery(statement, language); QueryResult result = q.execute(); return result.getNodes(); } catch (RepositoryException e) { e.printStackTrace(); return null; } } // public static final void exportXml(final String absPath, final OutputStream out, final boolean skipBinary) // throws IOException, RepositoryException { // if (isHippoRepository()) { // ((HippoSession) session).exportDereferencedView(absPath, out, skipBinary, false); // } else { // session.exportSystemView(absPath, out, skipBinary, false); // } // } // // public static final void importXml(String parentAbsPath, InputStream in, int uuidBehavior, int referenceBehavior, // int mergeBehavior) throws IOException, RepositoryException { // if (isHippoRepository()) { // ((HippoSession) session).importDereferencedXML(parentAbsPath, in, uuidBehavior, referenceBehavior, // mergeBehavior); // } else { // session.importXML(parentAbsPath, in, uuidBehavior); // } // } public static final String fullName(final Item item) throws RepositoryException { StringBuffer buf = new StringBuffer(); if (item.getDepth() == 0) { buf.append('/'); } String path = item.getPath(); buf.append(path.substring(path.lastIndexOf('/') + 1)); return buf.toString(); } }